package com.eduagent.xwqeduagent.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.safety.Safelist;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;

/**
 * 医院病历HTML转JSON服务
 * 先用Jsoup清理HTML，再使用大模型进行结构化抽取
 */
@Service
@Slf4j
public class MedicalRecordExtractor {

    @Autowired
    private ChatClient chatClient;

    private final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 将HTML病历转换为结构化JSON
     * @param htmlContent 原始HTML内容
     * @return 结构化JSON字符串
     */
    public String extractMedicalRecordToJson(String htmlContent) {
        try {
            // 确保输入内容使用UTF-8编码
            String utf8Html = new String(htmlContent.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
            log.info("确保UTF-8编码，内容长度: {}", utf8Html.length());
            
            // 第一步：使用Jsoup清理HTML
            String cleanedHtml = cleanHtmlContent(utf8Html);
            log.info("HTML清理完成，长度: {}", cleanedHtml.length());

            // 第二步：调用大模型进行结构化抽取
            String jsonResult = extractStructuredData(cleanedHtml);
            log.info("结构化抽取完成");

            // 验证JSON格式并处理中文编码
            JsonNode jsonNode = objectMapper.readTree(jsonResult);
            String formattedJson = objectMapper.writeValueAsString(jsonNode);
            
            return formattedJson;
        } catch (Exception e) {
            log.error("病历转换失败", e);
            return "{\"error\": \"" + e.getMessage() + "\"}";
        }
    }

    /**
     * 使用Jsoup清理HTML内容
     * @param htmlContent 原始HTML
     * @return 清理后的文本
     */
    private String cleanHtmlContent(String htmlContent) {
        // 解析HTML文档，指定UTF-8编码
        Document doc = Jsoup.parse(htmlContent, "UTF-8");
        
        // 移除script和style标签
        doc.select("script, style, noscript, iframe").remove();
        
        // 提取主要内容，保留基本结构但移除不必要的属性
        String textContent = doc.body().html();
        
        // 保留表格结构但清理标签属性
        textContent = Jsoup.clean(textContent, Safelist.relaxed());
        
        // 进一步清理：移除多余空白，并确保UTF-8编码
        return textContent.trim().replaceAll("\\s+", " ");
    }

    /**
     * 调用大模型进行结构化抽取
     * @param cleanedHtml 清理后的HTML
     * @return JSON字符串
     */
    private String extractStructuredData(String cleanedHtml) {
        // 设计高质量提示词
        String prompt = buildPrompt(cleanedHtml);
        
        // 调用大模型
        String response = chatClient.prompt()
                .user(prompt)
                .call()
                .chatResponse()
                .getResult()
                .getOutput()
                .getText();
                
        // 提取JSON部分（去除可能的Markdown代码块标记）
        return extractJsonFromResponse(response);
    }
    
    /**
     * 构建高质量提示词
     * @param content 预处理后的内容
     * @return 提示词
     */
    private String buildPrompt(String content) {
        return """
               # 任务：医院病历结构化提取
               
               ## 背景
               你是一个专业的医疗数据结构化处理系统，擅长从非结构化或半结构化的病历中提取关键信息并转换为规范的JSON格式。
               
               ## 输入数据
               以下是一份医院病历内容，可能包含患者基本信息、病史、诊断结果、治疗方案等内容：
               
               ```
               %s
               ```
               
               ## 处理步骤
               1. 首先，仔细分析上述病历文本，识别出所有实际存在的信息类别和字段
               2. 确定每个字段的名称和对应内容
               3. 不要猜测或填充文本中不存在的信息
               4. 根据实际内容动态构建JSON结构
               5. 确保所有中文内容使用UTF-8编码正确处理，不要出现乱码
               
               ## 输出要求
               1. 你的输出应该包含两部分：
                  - 第一部分：识别出的字段列表（仅内部使用，不要输出）
                  - 第二部分：最终的JSON结构（这是唯一应该输出的内容）
               
               2. 对于JSON结构，参考以下模板，但只包含实际存在于文本中的字段：
                  ```json
                  {
                    "patientInfo": {
                      // 只包含实际存在的字段，如：姓名、性别、年龄、身份证号、病历号、电话等
                    },
                    "visitInfo": {
                      // 只包含实际存在的字段，如：就诊时间、科室、医生等
                    },
                    "medicalHistory": {
                      // 只包含实际存在的字段，如：主诉、现病史、既往史、过敏史等
                    },
                    "diagnosis": {
                      // 只包含实际存在的字段，如：主要诊断、其他诊断等
                    },
                    "treatment": {
                      "medications": [
                        // 药品列表，每项包含实际存在的字段，如：名称、剂量、频次等
                      ],
                      // 其他治疗信息，如：手术、建议等
                    },
                    "examinations": [
                      // 检查列表，每项包含实际存在的字段，如：检查名称、结果等
                    ],
                    // 可能的其他字段，根据实际内容添加
                  }
                  ```
               
               3. 对于可能的表格数据，应当提取为结构化的数组
               4. 字段命名应使用驼峰式命名法，保持一致性
               5. 所有字段均应为字符串类型，除非是明确的数组
               
               ## 示例字段说明（仅供参考，实际输出应基于文档内容）
               - patientInfo可能包含：name(姓名)、gender(性别)、age(年龄)、identityNo(身份证号)、medicalRecordNo(病历号)、phone(电话)等
               - visitInfo可能包含：visitTime(就诊时间)、department(科室)、doctor(医生)等
               - medicalHistory可能包含：chiefComplaint(主诉)、presentIllness(现病史)、pastHistory(既往史)、allergyHistory(过敏史)等
               - diagnosis可能包含：mainDiagnosis(主要诊断)、otherDiagnosis(其他诊断，数组)等
               - treatment可能包含：medications(用药，数组)、surgeries(手术，数组)、recommendations(医嘱，数组)等
               - examinations可能包含：多个检查项目，每项有name(名称)和result(结果)等
               
               ## 重要说明
               - 仅返回JSON对象，不要包含任何其他解释或分析
               - 确保JSON格式有效且符合规范，特别注意引号、逗号等符号的正确使用
               - 如果发现文档中包含上述模板未涵盖的重要医疗信息，可以适当添加相关字段
               - 对于不确定的信息，宁可不包含也不要猜测填充
               - 确保所有中文字符正确编码，使用UTF-8
               """.formatted(content);
    }
    
    /**
     * 从大模型响应中提取JSON部分
     * @param response 大模型响应
     * @return JSON字符串
     */
    private String extractJsonFromResponse(String response) {
        // 尝试提取JSON代码块
        if (response.contains("```json")) {
            response = response.substring(response.indexOf("```json") + 7);
            response = response.substring(0, response.indexOf("```"));
        } else if (response.contains("```")) {
            response = response.substring(response.indexOf("```") + 3);
            response = response.substring(0, response.indexOf("```"));
        }
        
        return response.trim();
    }
} 